/*
 * 文件作用：实现音频解码、控制、播放
 * 文件描述：实现类：
 *          类musicHangdle：音频的解码与播放控制
 *          类msuicProcessThread：音频解码线程
 *          类musicPlayThread：音频播放线程
 * 文件创建时间：2017-5-2
 * 文件创建者：wsgalaxy<wsgalaxy@163.com>
 * 文件最后修改时间：
 * 文件修改者：
 * 文件协议：GPL
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
#include <libavutil/samplefmt.h>
}


#include <QApplication>
#include <QFile>
#include <QAudioFormat>
#include <QAudioOutput>
#include <QByteArray>
#include <QThread>
#include <QAudioFormat>
#include <QDir>

#define MAX_AUDIO_FRAME_SIZE 19200



#include "musicHandle.h"



musicProcessThread::musicProcessThread(QObject *parent)
{}

void musicProcessThread::doWork(musicHandle *mh)
{
    musicHandle *p = mh;
    AVFormatContext *pFormatCtx;
    int i, audioStream;
    AVCodecContext *pCodecCtx;
    AVCodec *pCodec;
    AVPacket *pPacket;              //压缩的音频帧
    uint8_t *out_buffer;
    AVFrame *pFrame;                //未压缩的音频帧

    int ret;
    uint32_t len = 0;
    int got_picture;
    int index = 0;
    int64_t in_channel_layout;
    struct SwrContext *au_convert_ctx;


    FILE *pFile = fopen("tmpOutput.pcm", "wb");
    //char url[] = "input.mp3";

    char *musicPath;

    char* tmpStr = QDir::toNativeSeparators(QString(p->musicName)).toUtf8().data();
    int tmpLen = strlen(tmpStr)+1;
    musicPath = new char[tmpLen];
    musicPath[tmpLen] = 0;
    strcpy(musicPath, tmpStr);

    if(musicPath[0] == '\\')
    {
        musicPath ++;
    }

    av_register_all();
    avformat_network_init();
    pFormatCtx = avformat_alloc_context();

    if(avformat_open_input(&pFormatCtx, /*p->musicName*/musicPath, NULL, NULL) != 0)      //读取文件头获取格式
    {
        printf("%s input stream error\n", musicPath);
        return ;
    }

    if(avformat_find_stream_info(pFormatCtx, NULL) < 0)             //获取流信息
    {
        printf("stream info error\n");
        return ;
    }

    av_dump_format(pFormatCtx, 0, /*p->musicName*/musicPath, 0);              //显示文件信息

    audioStream = -1;
    for(i = 0; i < pFormatCtx->nb_streams; i ++)        //获取音频流
    {
        if(pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            audioStream = i;
            break;
        }
    }

    if(audioStream == -1)
    {
        printf("no audio stream\n");
        return ;
    }

    pCodecCtx = pFormatCtx->streams[audioStream]->codec;
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);     //获取解码器

    if(pCodec == NULL)
    {
        printf("codec no found\n");
        return ;
    }

    if(avcodec_open2(pCodecCtx, pCodec, NULL) < 0)          //初始化解码器
    {
        printf("open codec error\n");
        return ;
    }

    pPacket = (AVPacket *) av_malloc(sizeof(AVPacket));
    av_init_packet(pPacket);                                //初始化压缩音频帧

    uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;      //设置双声道
    int out_nb_sample = pCodecCtx->frame_size;              //音频帧中每个声道的样本数
    enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16; //音频样本编码
    int out_sample_rate = 44100;                            //音频样本频率
    int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);           //声道数

    int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_sample, out_sample_fmt, 1); //获取给定声道下的缓冲区大小
    out_buffer = (uint8_t *) av_malloc(MAX_AUDIO_FRAME_SIZE * 2);       //分配缓冲区（2声道）
    pFrame = av_frame_alloc();                                          //分配音频帧

    in_channel_layout = av_get_default_channel_layout(pCodecCtx->channels);     //输入的音频文件的声道数

    au_convert_ctx = swr_alloc();
    au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, out_channel_layout,
                                        out_sample_fmt, out_sample_rate,
                                        in_channel_layout, pCodecCtx->sample_fmt,
                                        pCodecCtx->sample_rate, 0, NULL);

    swr_init(au_convert_ctx);           //重采样初始化？？


    while(av_read_frame(pFormatCtx, pPacket) >= 0)                      //读取下一帧
    {
        if(pPacket->stream_index == audioStream)                        //读到音频帧
        {
            ret = avcodec_decode_audio4(pCodecCtx, pFrame, &got_picture, pPacket);  //从压缩的packet中解码出音频到帧中

            if(ret < 0)                             //发生错误
            {
                printf("error decoding frame\n");
                return ;
            }

            if(got_picture > 0)                     //有帧被解码，为0表示从packet中未解码出帧
            {
                swr_convert(au_convert_ctx, &out_buffer, MAX_AUDIO_FRAME_SIZE,
                            (const uint8_t **)pFrame->data, pFrame->nb_samples);    //将解码出的原始数据源转换为所需的类型

                printf("index %5d\t pts %11d\t packet size %d\n", index, pPacket->pts, pPacket->size);
                fwrite(out_buffer, 1, out_buffer_size, pFile);                      //将得到的帧写到文件中
                index ++;
            }
        }
        av_free_packet(pPacket);                    //释放压缩音频包
        //obSize = out_buffer_size;
    }


    //程序后处理

    swr_free(&au_convert_ctx);

    fclose(pFile);
    av_free(out_buffer);
    avcodec_close(pCodecCtx);
    avformat_close_input(&pFormatCtx);
    avformat_network_deinit();

    //obSize = out_buffer_size;


    FILE *pf = fopen("tmpOutput.pcm", "rb");
    fseek(pf, 0, SEEK_END);
    p->pcmSize = ftell(pf);
    rewind(pf);

    p->pcmData = (char*) malloc(p->pcmSize);
    fread(p->pcmData, p->pcmSize, 1, pf);
    fclose(pf);
    remove("tmpOutput.pcm");

    p->sumTimeSec = p->pcmSize / 176400;
    p->isMusicProcessed = 1;

#ifdef Q_OS_WIN
    musicPath --;
#endif
    delete [] musicPath;
    emit processFinished();

    //return 0;
}


musicPlayThread::musicPlayThread(QObject *parent)
{
    startTimeSec = 0;
    count = 0;
}

void musicPlayThread::doWork(musicHandle *mh)
{
    musicHandle *p = mh;
    //if()

    while(!p->isMusicProcessed)
    {
        QThread::msleep(10);
    }

    QByteArray buffer;
    buffer.clear();

    count = p->startTimeSec * 100;

    while (count * 1764 < p->pcmSize)
    {

        while(p->state != PLAY)
        {
            if(p->state == DESTROY)
            {
                p->state = DESTROIED;
                emit stateDestroy();
                return;
            }
            QThread::msleep(10);
            //count = p->startTimeSec * 100;
        }

        while(buffer.size() < p->pcmOutput->periodSize())
        {
            buffer.append(p->pcmData + 1764*count, 1764);
            count ++;
            if(count * 1764 >= p->pcmSize)
            {
                break;
            }
            emit currentTimeChanged((int)(count / 100));
            //usedSize += 1764;
        }

        int chunk = p->pcmOutput->bytesFree() / p->pcmOutput->periodSize();
        while(chunk)
        {
            if(buffer.size() > p->pcmOutput->periodSize())
            {
                //p->pcmDevice->write(buffer.data(), 1764);
                p->pcmDevice->write(buffer.data(), p->pcmOutput->periodSize());
                //buffer = buffer.mid(p->pcmOutput->periodSize());
                buffer = buffer.mid(p->pcmOutput->periodSize());
            }
            else
            {
                p->pcmDevice->write(buffer);
                buffer.clear();
                break;
            }
            chunk --;
            //chunk = audio->bytesFree() / audio->periodSize();
        }

        //usleep(5000);
#ifndef Q_OS_WIN
        QThread::usleep(5000);
#endif
        if(count * 1764 >= p->pcmSize)
        {
            p->state = PAUSE;
            count = 0;
            emit statePause();
        }

    }
}


void musicPlayThread::changeCount(uint32_t c)
{
    count = c;
}



musicHandle::musicHandle(QObject *parent) : QObject(parent)
{
    isMusicSeted = 0;
    musicName = NULL;
    pcmSize = 0;
    pcmData = NULL;

    state = PAUSE;

    volume = 1;
    sumTimeSec = 0;
    currentTimeSec = 0;
    startTimeSec = 0;
    pcmDevice = NULL;
    isMusicProcessed = 0;

    QAudioFormat audiofmt;
    audiofmt.setSampleRate(44100);
    audiofmt.setChannelCount(2);
    audiofmt.setSampleSize(16);
    audiofmt.setCodec("audio/pcm");
    audiofmt.setByteOrder(QAudioFormat::LittleEndian);
    audiofmt.setSampleType(QAudioFormat::SignedInt);

    pcmOutput = new QAudioOutput(audiofmt);
    pcmDevice = pcmOutput->start();


    playThread = NULL;
    processThread = NULL;
    musicProcessDowork = NULL;
    musicPlayDowork = NULL;




}

musicHandle::~musicHandle()
{
    while(state != DESTROIED){}
    if(processThread != NULL)
    {
        //if(processThread->is)
        processThread->quit();
        free(processThread);
    }
    if(playThread != NULL)
    {
        playThread->quit();
        free(playThread);
    }

    if(musicName != NULL)
    {
        delete [] musicName;
    }
    if(pcmData != NULL)
    {
        free(pcmData);
    }
    if(pcmOutput != NULL)
    {
        pcmOutput->stop();
        delete pcmOutput;
    }

    if(musicPlayDowork != NULL)
        delete musicPlayDowork;
    if(musicProcessDowork != NULL)
        delete musicProcessDowork;
}

int musicHandle::setMusic(char *url)
{
    if(musicName != NULL)
    {
        //free(musicName);
        delete [] musicName;
        musicName = NULL;
    }
    //musicName = (char*) malloc(strlen(url)+1);
    musicName = new char[strlen(url)+1];
    memcpy(musicName, url, strlen(url)+1);
    isMusicSeted = 1;
    return 0;
}

int musicHandle::musicProcess()
{
    info.readInfo(musicName);

    if(this->processThread == NULL)
    {
        this->processThread = new QThread;
        this->musicProcessDowork = new musicProcessThread(this);
        connect(this, SIGNAL(process(musicHandle*)), this->musicProcessDowork, SLOT(doWork(musicHandle*)));
        this->musicProcessDowork->moveToThread(this->processThread);
        this->processThread->start();
    }

    emit process(this);
    return 0;
}

int musicHandle::musicStart()
{


    this->state = PLAY;


    return 0;
}


int musicHandle::musicPlayThreadStart()
{

    if(this->playThread == NULL)
    {
        this->playThread = new QThread;
        this->musicPlayDowork = new musicPlayThread(this);
        connect(this, SIGNAL(play(musicHandle*)), this->musicPlayDowork, SLOT(doWork(musicHandle*)));
        connect(musicPlayDowork, SIGNAL(currentTimeChanged(int)), this, SIGNAL(sendCurrentTime(int)));
        this->musicPlayDowork->moveToThread(playThread);
        this->playThread->start();
        emit play(this);
    }
}

int musicHandle::musicPause()
{
    this->state = PAUSE;
    return 0;
}


int musicHandle::musicDestroy()
{
    this->state = DESTROY;
    return 0;
}

int musicHandle::musicStop()
{}

uint32_t musicHandle::getSumTimeSec()
{}

int musicHandle::musicSetTimeSec(int t)
{
    startTimeSec = t;
    musicPlayDowork->changeCount(t * 100);
    return 0;
}

int musicHandle::musicSetVolum(double v)
{}

void musicHandle::musicProcessFinished()
{}

void musicHandle::currentTimeChanged(uint32_t t)
{}

